/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dameki.ki;

import dameki.core.GlobalVars;
import dameki.game.GameMove;
import dameki.game.Stone;
import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author Tobi
 */
public class AgentCalculator {
    
    public AgentCalculator(int level){
        this.step_into_level = level;
    }
    private int step_into_level;    
    
    public GameMove[] getMove(AgentPlayer player,Stone[][] field){
        
        ArrayList<ValuedGameMove> possible_moves =  new ArrayList<>();
        ArrayList<ValuedGameMove> jumps = new ArrayList<>();
        for(Stone s:player.getStoneList()){
            ValuedGameMove move = getBestMove(s, field, player.col);
            if(move!=null){
                if(Math.abs(move.getMoves()[0].getPosX()-move.getMoves()[0].getStone().getPosX())>1){
                    if((jumps.isEmpty() || jumps.get(0).getValue()<move.getValue()))
                        jumps.add(0, move);
                    else{
                        jumps.add(move);
                    }
                }
                if((possible_moves.isEmpty() || possible_moves.get(0).getValue()<move.getValue()))
                    possible_moves.add(0, move);
                else{
                    possible_moves.add(move);
                }
            }
        }
        if(possible_moves.isEmpty())
            return null;
        else{
            if(jumps.isEmpty()){
                ArrayList<GameMove[]> best = new ArrayList<>();
                for(int x=0;x<possible_moves.size();x++){
                    if(possible_moves.get(x).getValue() == possible_moves.get(0).getValue()){
                        best.add(possible_moves.get(x).getMoves());
                    }
                }
                return best.get(new Random().nextInt(best.size()));
            }
            else return jumps.get(0).getMoves();
        }        
    }
    
    private ValuedGameMove getBestMove(Stone s,Stone[][] field,int col){
        return getBestMove(s, null,field, col,false,step_into_level);
    }
    
    private ValuedGameMove getBestMove(Stone s,Stone[][] field,int col,int level){
        return getBestMove(s, null,field, col,false,level);
    }
    
    private ValuedGameMove getBestMove(Stone s,int[] pos,Stone[][] field,int col,boolean jumped,int level){
        int posX,posY;
        if(pos!=null){
            posY=pos[0];
            posX=pos[1];
        }else{
            posX=s.getPosX();
            posY=s.getPosY();
        }
        
        ValuedGameMove result = null;
        //if no Dame
        try {
            //top right empty//bottom left
            if (field[posY + col][posX + 1] == null) {
                if (!jumped) {
                    GameMove potenialMove = new GameMove(s, posY + col, posX + 1);
                    ValuedGameMove temp =  new ValuedGameMove(potenialMove,getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_MOVED,level,col));
                    if(result == null || result.getValue()<temp.getValue()){
                        result = temp;
                    }
                }
            }//enemy at top right
            else if (field[posY + col][posX + 1].getColor() != s.getColor()) {
                //no stone at 2top 2right
                if (field[posY + 2 * col][posX + 2] == null) {
//                    if(level==4 && col==-1){
//                System.out.print("");
//            }
                    GameMove potenialMove = new GameMove(s, posY + 2 * col, posX + 2);
                    ValuedGameMove temp; 
                    ValuedGameMove temp2 = getBestMove(s,new int[]{posY + 2 * col, posX + 2}, getNewFieldState(potenialMove, field), col, true, level);
                    if(temp2!=null){
                        temp = new ValuedGameMove(potenialMove,GlobalVars.STONE_KILLED);
                        temp.joinValuedGameMoves(temp2);                        
                    }else{
                        temp = new ValuedGameMove(potenialMove, getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_KILLED, level,col));                        
                    }                    
                    if(result == null || result.getValue()<temp.getValue()){
                        result = temp;
                    }
                }
            }
        } catch (IndexOutOfBoundsException ex) {
        }
        try {
            //top left empty
            if (field[posY + col][posX - 1] == null) {
                if (!jumped) {
                    GameMove potenialMove = new GameMove(s, posY + col, posX - 1);
                    ValuedGameMove temp =  new ValuedGameMove(potenialMove,getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_MOVED,level,col));
                    if(result == null || result.getValue()<temp.getValue()){
                        result = temp;
                    }
                }
            } //enemy at top left // top right
            else if (field[posY + col][posX - 1].getColor() != s.getColor()) {
                //no stone at 2top 2left
                if (field[posY + 2 * col][posX - 2] == null) { 
                    
                    GameMove potenialMove = new GameMove(s, posY + 2 * col,posX - 2);
                    ValuedGameMove temp; 
                    ValuedGameMove temp2 = getBestMove(s,new int[]{posY + 2 * col,posX - 2}, getNewFieldState(potenialMove, field), col, true, level);
                    if(temp2!=null){
                        temp = new ValuedGameMove(potenialMove,GlobalVars.STONE_KILLED);
                        temp.joinValuedGameMoves(temp2);                        
                    }else{
                        temp = new ValuedGameMove(potenialMove, getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_KILLED, level,col));                        
                    }                    
                    if(result == null || result.getValue()<temp.getValue()){
                        result = temp;
                    }
                }
            }
        } catch (IndexOutOfBoundsException ex) {
        }
        //if Dame
        if (s.IsDame()) {
            try {
                //top right empty
                if (field[posY - col][posX + 1] == null) {
                    if (!jumped){
                       GameMove potenialMove = new GameMove(s, posY - col,posX + 1);
                        ValuedGameMove temp =  new ValuedGameMove(potenialMove,getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_MOVED,level,col));
                        if(result == null || result.getValue()<temp.getValue()){
                            result = temp;
                        }
                    }
                }//enemy at top right
                else if (field[posY - col][posX + 1].getColor() != s.getColor()) {
                    //no stone at 2top 2right
                    if (field[posY - 2 * col][posX + 2] == null) {
                        GameMove potenialMove = new GameMove(s, posY - 2 * col, posX + 2);
                        ValuedGameMove temp; 
                        ValuedGameMove temp2 = getBestMove(s,new int[]{posY - 2 * col, posX + 2}, getNewFieldState(potenialMove, field), col, true, level);
                        if(temp2!=null){
                            temp = new ValuedGameMove(potenialMove,GlobalVars.STONE_KILLED);
                            temp.joinValuedGameMoves(temp2);                        
                        }else{
                            temp = new ValuedGameMove(potenialMove, getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_KILLED, level,col));                        
                        }                    
                        if(result == null || result.getValue()<temp.getValue()){
                            result = temp;
                        }
                    }
                }
            } catch (IndexOutOfBoundsException ex) {
            }
            try {
                //top left empty
                if (field[posY - col][posX - 1] == null) {
                    if (!jumped){
                        GameMove potenialMove = new GameMove(s, posY - col, posX - 1);
                        ValuedGameMove temp =  new ValuedGameMove(potenialMove,getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_MOVED,level,col));
                        if(result == null || result.getValue()<temp.getValue()){
                            result = temp;
                        }
                    }
                } //enemy at top left // top right
                else if (field[posY - col][posX - 1].getColor() != s.getColor()) {
                    //no stone at 2top 2left
                    if (field[posY - 2 * col][posX - 2] == null) {
                        GameMove potenialMove = new GameMove(s, posY - 2 * col,posX - 2);
                        ValuedGameMove temp; 
                        ValuedGameMove temp2 = getBestMove(s,new int[]{posY - 2 * col,posX - 2}, getNewFieldState(potenialMove, field), col, true, level);
                        if(temp2!=null){
                            temp = new ValuedGameMove(potenialMove,GlobalVars.STONE_KILLED);
                            temp.joinValuedGameMoves(temp2);                        
                        }else{
                            temp = new ValuedGameMove(potenialMove, getDetailFieldPoints(potenialMove, getNewFieldState(potenialMove, field), GlobalVars.STONE_KILLED, level,col));                        
                        }                    
                        if(result == null || result.getValue()<temp.getValue()){
                            result = temp;
                        }
                    }
                }
            } catch (IndexOutOfBoundsException ex) {
            }
        }
        return result;
    }
    
    private int getDetailFieldPoints(GameMove move,Stone[][] field, int startpoints,int level,int col){
        int value = 0;        
        if(!move.getStone().IsDame() && move.getPosY() == (col==1?7:0))
            value += GlobalVars.STONE_NEW_DAME;
        else if(move.getStone().IsDame() && move.getPosY() == (col==1?7:0))
            value += GlobalVars.STONE_DAME_TWICE;
        //fields left above is empty
        if (getStone(move.getPosY() + col, move.getPosX() - 1, field) == null) {
            value += GlobalVars.STONE_SAVELY_MOVED;
        } //friend left above
        else if (getStone(move.getPosY() + col, move.getPosX() - 1, field).getColor() == intToBool(col)) {
            //save stone
            if (getStone(move.getPosY() + 2 * col, move.getPosX() - 2, field) != null && getStone(move.getPosY() + 2 * col, move.getPosX() - 2, field).getColor() != intToBool(col)) {
                value += GlobalVars.STONE_SAVED;
            } //savely moved
            else {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }
        } //enemy left above
        else if (getStone(move.getPosY() + col, move.getPosX() - 1, field).getColor() != intToBool(col)) {
            //friend/enemy right below (saves from beeing killed in next round)
            if (getStone(move.getPosY() - col, move.getPosX() + 1, field) != null) {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }//field right below is empty (next round killed...)
            else if (getStone(move.getPosY() - col, move.getPosX() + 1, field) == null) {
                value += GlobalVars.STONE_UNSAVELY_MOVED;
            }
        }
        //fields right above is empty
        if (getStone(move.getPosY() + col, move.getPosX() + 1, field) == null) {
            value += GlobalVars.STONE_SAVELY_MOVED;
        } //friend right above
        else if (getStone(move.getPosY() + col, move.getPosX() + 1, field).getColor() == intToBool(col)) {
            //save stone
            if (getStone(move.getPosY() + 2 * col, move.getPosX() + 2, field) != null && getStone(move.getPosY() + 2 * col, move.getPosX() + 2, field).getColor() != intToBool(col)) {
                value += GlobalVars.STONE_SAVED;
            } //savely moved
            else {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }
        } //enemy right above
        else if (getStone(move.getPosY() + col, move.getPosX() + 1, field).getColor() != intToBool(col)) {
            //friend/enemy left below (saves from beeing killed in next round)
            if (getStone(move.getPosY() - col, move.getPosX() - 1, field) != null) {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }//field right below is empty (next round killed...)
            else if (getStone(move.getPosY() - col, move.getPosX() - 1, field) == null) {
                value += GlobalVars.STONE_UNSAVELY_MOVED;
            }
        }
        //if dame left below
        if (getStone(move.getPosY() - col, move.getPosX() - 1, field) != null && getStone(move.getPosY() - col, move.getPosX() - 1, field).IsDame() && getStone(move.getPosY() - col, move.getPosX() - 1, field).getColor() != intToBool(col)) {
            //friend/enemy right above (saves from beeing killed in next round)
            if (getStone(move.getPosY() + col, move.getPosX() + 1, field) != null) {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }//field right below is empty (next round killed...)
            else if (getStone(move.getPosY() + col, move.getPosX() + 1, field) == null) {
                value += GlobalVars.STONE_UNSAVELY_MOVED;
            }
        }//save left below
        else {
            value += GlobalVars.STONE_SAVELY_MOVED;
        }
        //if dame right below
        if (getStone(move.getPosY() - col, move.getPosX() + 1, field) != null && getStone(move.getPosY() - col, move.getPosX() + 1, field).IsDame() && getStone(move.getPosY() - col, move.getPosX() + 1, field).getColor()!=intToBool(col)) {
            //friend/enemy left above (saves from beeing killed in next round)
            if (getStone(move.getPosY() + col, move.getPosX() - 1, field) != null) {
                value += GlobalVars.STONE_SAVELY_MOVED;
            }//field right below is empty (next round killed...)
            else if (getStone(move.getPosY() + col, move.getPosX() - 1, field) == null) {
                value += GlobalVars.STONE_UNSAVELY_MOVED;
            }
        }//save left below
        else {
            value += GlobalVars.STONE_SAVELY_MOVED;
        }
        value = value - getEnemyPoints(field, col, level)/2;
        return value + startpoints;
    }
    
    private int getEnemyPoints(Stone[][] field,int owncol,int level){
        if(level>1){
            ArrayList<Stone> enemyStones = getEnemyStones(field, owncol);
            ValuedGameMove bestmove = null;
            for(Stone s :enemyStones){
                ValuedGameMove move = getBestMove(s, field, owncol*(-1), (level-1));
                if(move != null && (bestmove==null || move.getValue()>bestmove.getValue()))
                    bestmove = move;
            }
            if(bestmove==null)
                return 0;
            else
                return bestmove.getValue();
        }
        return 0;
    }
    
    private Stone[][] getNewFieldState(GameMove move,Stone[][] field){
        Stone[][] tempfield = new Stone[8][8];
        for(int z=0;z<field.length;z++){
            System.arraycopy(field[z], 0, tempfield[z], 0, field[z].length);
        }
        int Ydist = move.getStone().getPosY()-move.getPosY();
        int Xdist = move.getStone().getPosX()-move.getPosX();
        
        if(Xdist==2 || Xdist==-2){
            tempfield[move.getStone().getPosY()-Ydist/2][move.getStone().getPosX()-Xdist/2]=null;
        }
        tempfield[move.getStone().getPosY()][move.getStone().getPosX()]=null;
        tempfield[move.getPosY()][move.getPosX()]=move.getStone();
        return tempfield;
    }   
    
    private Stone getStone(int y, int x, Stone[][] field) {
        try {
            return field[y][x];
        } catch (IndexOutOfBoundsException ex) {
            return null;
        }
    }
    
    private boolean intToBool(int i){
        return i>0?true:false;
    }
    
    private ArrayList<Stone> getEnemyStones(Stone[][] field,int col){
        ArrayList list =  new ArrayList();
        for(int y=0;y<field.length;y++){
            for(int x=0;x<field[y].length;x++){
                if(field[y][x]!=null && field[y][x].getColor()!=intToBool(col)){
                    list.add(field[y][x]);
                }
            }
        }
        return list;
    }
}
